/*
Interactive - Easy create interactive user interfaces 
<http://interactive-js.googlecode.com/>

License:
    The MIT License
    
    Copyright (c) 2008 Alex Indigo <iam@alexindigo.com>
    
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.

Require:
    - Prototype 1.6 or greater <http://www.prototypejs.org/>

Inspiration:
    - Prototype JavaScript framework <http://www.prototypejs.org/> [Copyright (c) 2005-2008 Sam Stephenson]
    - MooTools <http://mootools.net/> [Copyright (c) 2006-2007 Valerio Proietti]
    - jQuery <http://jquery.com/> [Copyright (c) 2008 John Resig]
    - Douglas Crockford <www.crockford.com>

TODO:
    - Do some nice functions/objects for error throwing
*/

(function(){
/* lambda wrapper begins */

// TODO: Check for existing object window.IA
// if we need it at all

var userConfig = {
// {{{ user defined config
    lang: 'en',
    currency: {
        text: 'CAD',
        mark: '$'
    }
// }}}
};

// TODO: Add constructor and check for Prototype object
// TODO: If no Prototype object load it thru google interface. Eh?

var Interactive = function(user)
{
    // store reference to main object
    var that = this;

    // current version
    this.version = '0.1',

    // {{{ config
    // TODO: Make deep config merger
    this.config = {

        // current language
        lang: user.lang || 'en',

        // local currency
        currency: user.currency || {
            text: 'CAD',
            symbol: '$'
        }
    };
    // }}}

    // {{{ Form
    this.Form = {};

    // messages
    this.Form.messages = {
        en: {
        
            // stub messages
            'units':                                'units',
            'thousands':                            'thousands',

            // error messages
            'should_be_valid_price':                'Should be a valid price in ' + that.config.currency.text,
            'should_be_number':                     'Should be a number',
            'should_be_number_by_thousands':        'Should be a number by thousands'
        },

        fr: {
            
        }
    };

    // field types
    this.Form.types = {

        'price':
        {
            stub: that.config.currency.text,
            message: that.Form.messages[that.config.lang]['should_be_valid_price'],
            pattern: /^[0-9\$\,\.\ ]+$/,
            format: function(value)
            {
                value = that.Utils.parsePrice(value);
                return that.Utils.printPrice(value, ',');
            },
            data: function(value)
            {
                return that.Utils.parsePrice(value);
            }
        },

        'number':
        {
            stub: that.Form.messages[that.config.lang]['units'],
            message: that.Form.messages[that.config.lang]['should_be_number'],
            pattern: /^[0-9\,\.\ ]+$/,
            format: function(value)
            {
                value = that.Utils.parseNumber(value);
                return that.Utils.printNumber(parseInt(value, 10), ',');
            },
            data: function(value)
            {
                return that.Utils.parseNumber(value);
            }
        },

        'number-by-thousand':
        {
            stub: that.Form.messages[that.config.lang]['thousands'],
            message: that.Form.messages[that.config.lang]['should_be_number_by_thousands'],
            pattern: /^[0-9\,\.\ ]+(\s*)(M?)$/i,
            format: function(value)
            {
                value = that.Utils.parseNumber(value, 1000);
                return that.Utils.printNumber(parseInt(value, 10), ',');
            },
            data: function(value)
            {
                return that.Utils.parseNumber(value);
            }
        },
// CONTINUE
        'field-time':
        {
            'stub': 'HH:MM:SS am',
            'error': 'Should be a time',
            'pattern': /^[0-2]?[0-9]:[0-5]?[0-9](:[0-5]?[0-9])?([\ ]*(am|pm))?$/i,
            'format': function(value)
            {
                return parseTime(value);
            },
        },
    
        'field-time-by-quaters':
        {
            'stub': 'HH:MM am',
            'error': 'Should be a hours and minutes',
            'pattern': /^[0-2]?[0-9]:[0-5]?[0-9]([\ ]*(am|pm))?$/i,
            'format': function(value)
            {
                return parseTime(value, 1, 15);
            }
        },
    
        'field-hours-by-quaters':
        {
            'stub': 'HH:MM',
            'error': 'Should be a hours and minutes',
            'pattern': /^[0-9]?[0-9]?(:|\.)?[0-9]?[0-9](:[0-5]?[0-9])?$/i,
            'format': function(value)
            {
                return parseHours(value, 1, 15);
            }
        },
    
        'field-size-2d':
        {
            'stub': 'inch x inch',
            'error': 'Should specify width and height in inches.',
            'pattern': /^([0-9\.]+)((\s*)x(\s*))([0-9\.]+)$/
        },
    
        'field-date':
        {
            'stub': 'MM/DD/YYYY',
            'error': 'Please enter the date in following format or choose one form calendar. MM/DD/YYYY',
            'pattern': /^([0-1]*[0-9])\/([0-3]*[0-9])\/([0-9]){2,4}$/
        },
    
        'field-paper-type':
        {
            'stub': '',
            'error': 'Please enter paper type'
        },
    
        'field-text':
        {
            'stub': '',
            'error': 'Should be a text'
        }
    };
    // }}}

    // {{{ Controls
    this.Control = {};
    // }}}

    // {{{ Utils
    // TODO: Extend String object with these methods
    this.Utils = {

        // TODO: Refactor!
        // parse given string for time
        parseTime: function(str)
        {
            // strip spaces if so
            var v = str.replace(/[ ]/, '');
        
            var charPos = v.search(/[^\d^:]/);
        
            var i = 0;
            var s= '';
            var factor,time,h,m;
        
            if (v.length === 0)
            {
                // I don't know why, but it should like this
                // don't assign anything to number
            }
            else if (charPos > 0)
            {
                // have some factor
                factor = v.substring(charPos).toUpperCase();
                v = v.substring(0, charPos);
            }
            else if (charPos === 0)
            {
                // shouldn't be something like that
                time = [];
                time.error = 'Not a proper time';
                return time;
            }
        
            time = v.split(':');
        
            switch (time.length)
            {
                case 3:
                    s = parseInt(time[2], 10);
                case 2:
                    h = parseInt(time[0], 10);
                    m = parseInt(time[1], 10);
                break;
        
                default:
                    // shouldn't be something like that
                    time = [];
                    time.error = 'Not a proper time';
                    return time;
            }
        
            if (factor)
            {
                if (h > 12 || h < 1) 
                {
                    // shouldn't be something like that
                    time = [];
                    time.error = 'Not a proper time';
                    return time;
                }
                else if (factor == 'PM')
                {
                    h = h+12;
                }
            }
        
            // by hours
            if (arguments[1])
            {
                if (h % arguments[1] !== 0) 
                {
                    h = (h/arguments[1]).round() * arguments[1];
                }
            }
        
            // by minutes
            if (arguments[2])
            {
                if (m % arguments[2] !== 0) 
                {
                    m = (m/arguments[2]).round() * arguments[2];
                }
            }
        
            // by seconds
            if (s && arguments[3])
            {
                if (s % arguments[3] !== 0) 
                {
                    s = (s/arguments[3]).round() * arguments[3];
                }
        
                if (s < 10) 
                {
                    s = '0' + s;
                }
        
                s = ':'+s;
            }
        
            if (h > 12) 
            {
                h = h - 12;
                factor = 'PM';
            }
            else 
            {
                if (h == 0) 
                {
                    h = 12;
                }
                factor = 'AM';
            }
        
            if (h < 10) 
            {
                h = '0' + h;
            }
            if (m < 10) 
            {
                m = '0' + m;
            }
        
            time = h+':'+m+s+' '+factor;
        
            return time;
        },

        // TODO: Refactor!
        // TODO: Combine parseHours and parseTime together
        parseHours: function(str)
        {
            var i = 0;
            var s='';
            var m='';
            var h='';
            var time,nums;
        
            // strip spaces if so
            var v = str.replace(/[\ ]/g, '');
        
            if (v.length === 0)
            {
                // I don't know why, but it should like this
                // don't assign anything to number
                return false;
            }
        
            // convert decimal notation to time notation
            if (v.search(/\./) > -1)
            {
                nums = v.split('.', 2);
        
                nums[0] = parseInt(nums[0], 10) || '0';
                nums[1] = (60 * (parseInt(nums[1], 10)/Math.pow(10,nums[1].length))).round();
        
                v = nums[0] + ':' + nums[1];
            }
        
            time = v.split(':');
        
            switch (time.length)
            {
                case 3:
                    s = parseInt(time[2], 10) || '00';
                case 2:
                    h = parseInt(time[0], 10) || '00';
                    m = parseInt(time[1], 10) || '00';
                break;
                case 1:
                    h = parseInt(time[0], 10) || '00';
                    m = '00';
                break;
        
                default:
                    // shouldn't be something like that
                    time = [];
                    time.error = 'Not a proper time';
                    return time;
            }
        
            // special treat for minutes
            if (m > 59) 
            {
        
                h = parseInt(h, 10) + (m/60).floor();
                m = m % 60 || '00';
            }
        
        
            // by hours
            if (h && arguments[1]) 
            {
                if (h % arguments[1] !== 0) 
                {
                    h = (h / arguments[1]).ceil() * arguments[1];
                }
                if (h < 10) 
                {
                    h = '0' + parseInt(h, 10);
                }
            }
            else 
            {
                h = '';
            }
        
            // by minutes
            if (m && arguments[2])
            {
                if (m % arguments[2] !== 0) 
                {
                    m = (m/arguments[2]).ceil() * arguments[2];
                }
        
                if (m < 10) 
                {
                    m = '0' + parseInt(m, 10);
                }
        
                m = ':'+m;
            }
            else 
            {
                m = '';
            }
        
            // by seconds
            if (s && arguments[3]) 
            {
                if (s % arguments[3] !== 0) 
                {
                    s = (s / arguments[3]).ceil() * arguments[3];
                }
                
                if (s < 10) 
                {
                    s = '0' + parseInt(s, 10);
                }
                
                s = ':' + s;
            }
            else 
            {
                s = '';
            }
        
            time = h+m+s;
        
            return time;
        },

        // TODO: Refactor!
        // TODO: Combine them together
        // parse given string for number
        parseNumber: function(str)
        {
            // strip commas if so
            var v = str.replace(/[, ]/, '');
        
            var charPos = v.search(/[^\d^\.]/);
        
            var i = 0;
            var number;
        
            if (v.length === 0)
            {
                // I don't know why, but it should like this
                // don't assign anything to number
            }
            else if (charPos > 0)
            {
                // have some factor
                number = parseFloat(v.substring(0, charPos));
                var factor = v.substring(charPos);
        
                switch(factor)
                {
                    case 'M': // Mille- = 1 thousand
                    case 'm': // 
                        number = number * 1000;
                    break;    
                    default:
                        // something else, just skip it
                }
        
            }
            else if (charPos === 0)
            {
                // set number as zero
                number = [];
                number.error = 'Not a number';
            }
            else
            {
                // just number
                number = parseFloat(v);
        
                // if multiplyer is specified
                if (arguments[1])
                {
                    if (number % arguments[1] !== 0) 
                    {
                        number = number * arguments[1];
                    }
                }
            }
        
            return number;
        },

        // TODO: Refactor!
        // parse string for given price
        parsePrice: function(str)
        {
            // strip dollar sign
            return this.parseNumber(str.replace(/\$/, ''));
        },

        // TODO: Refactor!
        // output price as string
        printPrice: function(num)
        {
            num = parseFloat(num);
        
            if (num != 'NaN') 
            {
                var charPos = num.toString().search(/\.[0-9]{3,}/);
        
                if (charPos > 0) 
                {
                    num = parseFloat(num.toString().substring(0, charPos+3));
                }
        
                return '$' + this.printNumber(num, arguments[1]);
            }
            else 
            {
                return '';
            }
        },

        // TODO: Refactor!
        // output number as string
        printNumber: function(num)
        {
            var sep = ','; // default separator
        
            // if separator is specified
            if (arguments[1])
            {
                sep = arguments[1];
            }
        
            if (num.toString().length > 0)
            {
                // strip stuff
                num = parseFloat(num);
            
                var rem = '';
            
                var str = num.toString();
            
                if (num != parseInt(num, 10))
                {
                    rem = str.substring(str.indexOf('.'));
                    num = parseInt(num, 10);
                    str = num.toString();
                }
            
                var len = str.length;
                var ths = parseInt(len/3, 10);
            
                for (i=0; i<ths; i++)
                {
                
                    if ((i+1)*3 < len)
                    {
                        str = str.substring(0, str.length-((i+1)*3)-(sep.length*i)) + sep + str.substring(str.length-((i+1)*3)-(sep.length*i));
                    }
                }
            
                str = str + rem;
            }
            else
            {
                str = '';
            }
        
            return str;
        }
    };
    // }}} 
};

// {{{ go interactive
var IA = new Interactive(userConfig);
// }}}

/* lambda wrapper ends */
})();

/*
EOF
*/
